#include <errno.h>
#include <stdio.h>
#include <time.h>
#include "log.h"
#include "main.h"
#include "game.h"
#include "util/ipc.h"

LogLevel log_level = LOG_ERROR;

bool log_to_file = false;
bool log_to_stderr = false;
bool log_timestamp = true;
bool log_ticks = true;

static FILE* log_out = NULL;
static char timestamp_buffer [100];
static char message_buffer [LOG_BUFFER_SIZE];

static char* timestamp ()
{
	size_t cursor = 0;
	
	if (log_timestamp) {
		time_t t = time (NULL);
		struct tm* ltime = localtime (&t);
		timestamp_buffer[0] = '[';
		cursor = 1 + strftime (timestamp_buffer+1, 99, "%c", ltime);
		if (cursor == 1) {
			log_timestamp = false;
			log_ticks = false;
			log_fatal ("Failed to get timestamp.");
		}
		timestamp_buffer[cursor++] = ']';
		timestamp_buffer[cursor++] = ' ';
	}
	
	if (log_ticks && (main_state == STATE_GAME)) {
		cursor += sprintf (&timestamp_buffer[cursor], "[%d]", the_game.game_time);
		timestamp_buffer[cursor++] = ' ';
	}
	
	timestamp_buffer[cursor++] = '\0';
	
	assert(cursor <= 100);
	
	return timestamp_buffer;
}

void log_init (bool file_enabled, const char *filename, bool stderr_enabled,
               bool timestamp_enabled, bool ticks_enabled)
{
	assert(log_out == NULL); // need virgin log
	assert(!file_enabled || (filename != NULL));
	
	log_to_file = file_enabled;
	log_to_stderr = stderr_enabled;
	log_timestamp = timestamp_enabled;
	log_ticks = ticks_enabled;
	
	if (file_enabled) {
		log_out = fopen(filename, "a");
		
		if (log_out == NULL) {
			log_to_file = false;
			log_to_stderr = true;
			log_fatal ("Failed to open log file \"%s\": %s\n",
			           filename, strerror (errno));
		}
	} else {
		log_out = NULL;
	}
	
	log_info ("Log started.");
}

void log_cleanup ()
{
	fclose (log_out);
	log_to_file = false;
	log_to_stderr = false;
	log_timestamp = true;
	log_ticks = true;
	log_out = NULL;
}

static void write_message_buffer (const char* level, bool is_truncated)
{
	message_buffer[LOG_BUFFER_SIZE-1] = '\0';
	
	// FIXME: check return values of fprintf and fflush for errors
	if (log_to_file) {
		fprintf (log_out, "%s%s: %s\n", timestamp (), level, message_buffer);
		if (is_truncated) {
			log_error ("Previous message was truncated due to limit "
			           "of %d characters.\n", LOG_BUFFER_SIZE);
		}
		fflush (log_out);
	}
	
	if (log_to_stderr) {
		fprintf (stderr, "%s%s: %s\n", timestamp (), level, message_buffer);
		if (is_truncated) {
			log_error ("Previous message was truncated due to limit "
			           "of %d characters.\n", LOG_BUFFER_SIZE);
		}
	}
}

void log_debug (char* format, ...)
{
	if (log_level <= LOG_DEBUG) {
		va_list ap;
		va_start (ap, format);
		int printed = vsnprintf (message_buffer, LOG_BUFFER_SIZE, format, ap);
		va_end (ap);
		
		write_message_buffer ("DEBUG", printed >= LOG_BUFFER_SIZE);
	}
}

void log_info (char* format, ...)
{
	if (log_level <= LOG_INFO) {
		va_list ap;
		va_start (ap, format);
		int printed = vsnprintf (message_buffer, LOG_BUFFER_SIZE, format, ap);
		va_end (ap);
		
		write_message_buffer ("INFO", printed >= LOG_BUFFER_SIZE);
	}
}

void log_error (char* format, ...)
{
	if (log_level <= LOG_ERROR) {
		va_list ap;
		va_start (ap, format);
		int printed = vsnprintf (message_buffer, LOG_BUFFER_SIZE, format, ap);
		va_end (ap);
		
		write_message_buffer ("ERROR", printed >= LOG_BUFFER_SIZE);
	}
}

void log_fatal (char* format, ...)
{
	int e = errno;
	
	va_list ap;
	va_start (ap, format);
	int printed = vsnprintf (message_buffer, LOG_BUFFER_SIZE, format, ap);
	va_end (ap);
	
	if (log_level <= LOG_FATAL) {
		write_message_buffer ("FATAL", printed >= LOG_BUFFER_SIZE);
	}
	
	char screen_message[LOG_SCREENMSG_SIZE];
	char errno_str [50];
	snprintf (screen_message, LOG_SCREENMSG_SIZE, "Error: %s", message_buffer);
	screen_message[LOG_SCREENMSG_SIZE-1] = '\0';
	sprintf (errno_str, "errno = %d", e);
	
	while (!key[KEY_ESC]) {
		textout_centre_ex(screen, font, screen_message, SCREEN_W/2, SCREEN_H/2, makecol (255, 0, 0), makecol(0,0,0));
		textout_centre_ex(screen, font, errno_str, SCREEN_W/2, SCREEN_H/2 +20, makecol (255, 0, 0), makecol(0,0,0));
		textout_centre_ex(screen, font, strerror (e), SCREEN_W/2, SCREEN_H/2 +40, makecol (255, 0, 0), makecol(0,0,0));
		rest(1);
	}

	ipc_stop_server (); // Attempt to kill server if it exists. No blocks.
	allegro_exit ();
	exit (1);
}
